/* Copyright (C) 2007-2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
*/
#include "android/hw-events.h"
#include "android/charmap.h"
#include "android/globals.h"  /* for android_hw */
#include "user-events.h"
#include "console.h"

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <bank_defs.h>
#include <skyeye_sched.h>
#include <skyeye_options.h>
#include <skyeye_config.h>
#include <skyeye_command.h>
#include <skyeye_uart_ops.h>
#include <skyeye_class.h>
#include <skyeye_interface.h>
#include <skyeye_mm.h>
#include <skyeye_mach.h>
#include <skyeye_arch.h>
#include <skyeye_android_intf.h>

#include "goldfish_events.h"


enum {
    REG_READ        = 0x00,
    REG_SET_PAGE    = 0x00,
    REG_LEN         = 0x04,
    REG_DATA        = 0x08,

    PAGE_NAME       = 0x00000,
    PAGE_EVBITS     = 0x10000,
    PAGE_ABSDATA    = 0x20000 | EV_ABS,
};

/* These corresponds to the state of the driver.
 * Unfortunately, we have to buffer events coming
 * from the UI, since the kernel driver is not
 * capable of receiving them until XXXXXX
 */
enum {
    STATE_INIT = 0,  /* The device is initialized */
    STATE_BUFFERED,  /* Events have been buffered, but no IRQ raised yet */
    STATE_LIVE       /* Events can be sent directly to the kernel */
};


/* modify this each time you change the events_device structure. you
 * will also need to upadte events_state_load and events_state_save
 */
#define  EVENTS_STATE_SAVE_VERSION  2

extern const char*  android_skin_keycharmap;

void
goldfish_events_set_update_intr (int level)
{
	goldfish_events_device* dev;
	conf_object_t* obj = get_conf_obj("goldfish_events_0");
	dev = (goldfish_events_device *)obj->obj;

	struct machine_config *mc = (struct machine_config *) get_current_mach();
	printf("[skyeye]in %s,send events interrupt\n",__func__);
	mc->mach_set_intr (dev->extern_irq);

	if (level == 0) 
	      dev->master->raise_signal(dev->master->conf_obj,dev->internal_irq);
	else
	      dev->master->lower_signal(dev->master->conf_obj,dev->internal_irq);

}

static void enqueue_event(unsigned int type, unsigned int code, int value)
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    int  enqueued = s->last - s->first;

    if (enqueued < 0)
        enqueued += MAX_EVENTS;

    if (enqueued + 3 > MAX_EVENTS) {
        fprintf(stderr, "##KBD: Full queue, lose event\n");
        return;
    }

    if(s->first == s->last) {
	if (s->state == STATE_LIVE)
	{
		printf("[skyeye] events enabled\n");
		goldfish_events_set_update_intr (1);
	}

//	  qemu_irq_raise(s->irq);
	else {
		printf("[skyeye] events disabled\n");
	  s->state = STATE_BUFFERED;
	}
    }

    //fprintf(stderr, "##KBD: type=%d code=%d value=%d\n", type, code, value);

    s->events[s->last] = type;
    s->last = (s->last + 1) & (MAX_EVENTS-1);
    s->events[s->last] = code;
    s->last = (s->last + 1) & (MAX_EVENTS-1);
    s->events[s->last] = value;
    s->last = (s->last + 1) & (MAX_EVENTS-1);
}

static unsigned dequeue_event()
{
    unsigned n;

    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    if(s->first == s->last) {
	printf("[skyeye] in %s,queue is already empty\n",__func__);
        return 0;
    }

    n = s->events[s->first];

    printf("[skyeye]in %s\n",__func__);
    s->first = (s->first + 1) & (MAX_EVENTS - 1);

    if(s->first == s->last) {
        //qemu_irq_lower(s->irq);
	goldfish_events_set_update_intr (0);
    }
//#ifdef TARGET_I386
    /*
     * Adding the logic to handle edge-triggered interrupts for x86
     * because the exisiting goldfish events device basically provides
     * level-trigger interrupts only.
     *
     * Logic: When an event (including the type/code/value) is fetched
     * by the driver, if there is still another event in the event
     * queue, the goldfish event device will re-assert the IRQ so that
     * the driver can be notified to fetch the event again.
     */
    else if (((s->first + 2) & (MAX_EVENTS - 1)) < s->last ||
               (s->first & (MAX_EVENTS - 1)) > s->last) { /* if there still is an event */
//        qemu_irq_lower(s->irq);
	  printf("[skyeye]there are also events in the queue\n");
	  goldfish_events_set_update_intr (0);
	  goldfish_events_set_update_intr (1);
 //       qemu_irq_raise(s->irq);
    }
//#endif
    return n;
}

static const char*
get_charmap_name()
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    if (s->name != NULL)
        return s->name;

    s->name = android_get_charmap_name();
    printf("[skyeye] char map is %s\n",s->name);
    return s->name;
}


static int get_page_len()
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    int page = s->page;
    if (page == PAGE_NAME) {
        const char* name = get_charmap_name();
        return strlen(name);
    } if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
        return s->ev_bits[page - PAGE_EVBITS].len;
    if (page == PAGE_ABSDATA)
        return s->abs_info_count * sizeof(s->abs_info[0]);
    return 0;
}

static int get_page_data(int offset)
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    int page_len = get_page_len();
    int page = s->page;
    if (offset > page_len)
        return 0;
    if (page == PAGE_NAME) {
        const char* name = get_charmap_name();
        return name[offset];
    } if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
        return s->ev_bits[page - PAGE_EVBITS].bits[offset];
    if (page == PAGE_ABSDATA) {
        return s->abs_info[offset / sizeof(s->abs_info[0])];
    }
    return 0;
}

static exception_t events_read(conf_object_t *opaque, generic_address_t offset, void* value, size_t count)
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;


    /* This gross hack below is used to ensure that we
     * only raise the IRQ when the kernel driver is
     * properly ready! If done before this, the driver
     * becomes confused and ignores all input events
     * as soon as one was buffered!
     */
    if (offset == REG_LEN && s->page == PAGE_ABSDATA) {
	if (s->state == STATE_BUFFERED)
	{
		printf("[skyeye]events driver is already enabled\n");
		printf("[skyeye]we should to handler the events before\n");
		goldfish_events_set_update_intr (1);
	}
	  //qemu_irq_raise(s->irq);
	printf("[skyeye] events driver can handler the events now\n");
	s->state = STATE_LIVE;
    }

    if (offset == REG_READ)
        *(uint32_t *)value = dequeue_event();
    else if (offset == REG_LEN)
        *(uint32_t *)value = get_page_len();
    else if (offset >= REG_DATA)
        *(uint32_t *)value = get_page_data(offset - REG_DATA);
    return No_exp;
}

static exception_t events_write(conf_object_t* opaque, generic_address_t offset, void* value, size_t count) {

    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    uint32_t val = *(uint32_t*)value;
    if (offset == REG_SET_PAGE)
        s->page = val;
    return No_exp;
}


static void events_put_keycode(void *x, int keycode)
{
    events_state_t *s = (events_state_t *) x;
    printf("[skyeye key] in %s \n",__func__);
    enqueue_event(EV_KEY, keycode&0x1ff, (keycode&0x200) ? 1 : 0);
}

static void events_put_mouse(void *opaque, int dx, int dy, int dz, int buttons_state)
{

    events_state_t *s = (events_state_t *) opaque;
    /* in the Android emulator, we use dz == 0 for touchscreen events,
     * and dz == 1 for trackball events. See the kbd_mouse_event calls
     * in android/skin/trackball.c and android/skin/window.c
     */
    if (dz == 0) {
	printf("[skyeye touch]in %s,touchscreen moved\n",__func__);
        enqueue_event(EV_ABS, ABS_X, dx);
        enqueue_event(EV_ABS, ABS_Y, dy);
        enqueue_event(EV_ABS, ABS_Z, dz);
        enqueue_event(EV_KEY, BTN_TOUCH, buttons_state&1);
    } else {
	printf("[skyeye mouse]in %s,key pressed\n",__func__);
        enqueue_event(EV_REL, REL_X, dx);
        enqueue_event(EV_REL, REL_Y, dy);
    }
    enqueue_event(EV_SYN, 0, 0);
}

static void  events_put_generic(void*  opaque, int  type, int  code, int  value)
{

    events_state_t *s = (events_state_t *) opaque;
    enqueue_event(type, code, value);
}

/* set bits [bitl..bith] in the ev_bits[type] array
 */
static void
events_set_bits(int type, int bitl, int bith)
{
    uint8_t *bits;
    uint8_t maskl, maskh;
    int il, ih;

    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    il = bitl / 8;
    ih = bith / 8;
    if (ih >= s->ev_bits[type].len) {
        bits = qemu_mallocz(ih + 1);
        if (bits == NULL)
            return;
        memcpy(bits, s->ev_bits[type].bits, s->ev_bits[type].len);
        qemu_free(s->ev_bits[type].bits);
        s->ev_bits[type].bits = bits;
        s->ev_bits[type].len = ih + 1;
    }
    else
        bits = s->ev_bits[type].bits;
    maskl = 0xffU << (bitl & 7);
    maskh = 0xffU >> (7 - (bith & 7));
    if (il >= ih)
        maskh &= maskl;
    else {
        bits[il] |= maskl;
        while (++il < ih)
            bits[il] = 0xff;
    }
    bits[ih] |= maskh;
}

static void
events_set_bit(int  type, int  bit)
{
    events_set_bits(type, bit, bit);
}

static void
events_clr_bit(int type, int bit)
{
    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;

    int ii = bit / 8;
    if (ii < s->ev_bits[type].len) {
        uint8_t* bits = s->ev_bits[type].bits;
        uint8_t  mask = 0x01U << (bit & 7);
        bits[ii] &= ~mask;
    }
}

void events_dev_init()
{
    int iomemtype;

    conf_object_t* android = get_conf_obj("android_0");
    android_interface_t* android_if = SKY_get_interface(android, ANDROID_INTF_NAME);
    AndroidHwConfig*  config = android_if->get_android_hw();


    goldfish_events_device* dev;
    conf_object_t* obj = get_conf_obj("goldfish_events_0");
    dev = (goldfish_events_device *)obj->obj;
    events_state_t* s = dev->events;
    // charmap name will be determined on demand
    s->name = NULL;

    /* now set the events capability bits depending on hardware configuration */
    /* apparently, the EV_SYN array is used to indicate which other
     * event classes to consider.
     */

    /* configure EV_KEY array
     *
     * All Android devices must have the following keys:
     *   KEY_HOME, KEY_BACK, KEY_SEND (Call), KEY_END (EndCall),
     *   KEY_SOFT1 (Menu), VOLUME_UP, VOLUME_DOWN
     *
     *   Note that previous models also had a KEY_SOFT2,
     *   and a KEY_POWER  which we still support here.
     *
     *   Newer models have a KEY_SEARCH key, which we always
     *   enable here.
     *
     * A Dpad will send: KEY_DOWN / UP / LEFT / RIGHT / CENTER
     *
     * The KEY_CAMERA button isn't very useful if there is no camera.
     *
     * BTN_MOUSE is sent when the trackball is pressed
     * BTN_TOUCH is sent when the touchscreen is pressed
     */
    events_set_bit (EV_SYN, EV_KEY );

    events_set_bit(EV_KEY, KEY_HOME);
    events_set_bit(EV_KEY, KEY_BACK);
    events_set_bit(EV_KEY, KEY_SEND);
    events_set_bit(EV_KEY, KEY_END);
    events_set_bit(EV_KEY, KEY_SOFT1);
    events_set_bit(EV_KEY, KEY_VOLUMEUP);
    events_set_bit(EV_KEY, KEY_VOLUMEDOWN);
    events_set_bit(EV_KEY, KEY_SOFT2);
    events_set_bit(EV_KEY, KEY_POWER);
    events_set_bit(EV_KEY, KEY_SEARCH);

    if (config->hw_dPad) {
        events_set_bit(EV_KEY, KEY_DOWN);
        events_set_bit(EV_KEY, KEY_UP);
        events_set_bit(EV_KEY, KEY_LEFT);
        events_set_bit(EV_KEY, KEY_RIGHT);
        events_set_bit(EV_KEY, KEY_CENTER);
    }

    if (config->hw_trackBall) {
        events_set_bit(EV_KEY, BTN_MOUSE);
    }
    if (config->hw_touchScreen) {
        events_set_bit(EV_KEY, BTN_TOUCH);
    }

    if (config->hw_camera) {
        events_set_bit(EV_KEY, KEY_CAMERA);
    }

    if (config->hw_keyboard) {
        /* since we want to implement Unicode reverse-mapping
         * allow any kind of key, even those not available on
         * the skin.
         *
         * the previous code did set the [1..0x1ff] range, but
         * we don't want to enable certain bits in the middle
         * of the range that are registered for mouse/trackball/joystick
         * events.
         *
         * see "linux_keycodes.h" for the list of events codes.
         */
        events_set_bits(EV_KEY, 1, 0xff);
        events_set_bits(EV_KEY, 0x160, 0x1ff);

        /* If there is a keyboard, but no DPad, we need to clear the
         * corresponding bits. Doing this is simpler than trying to exclude
         * the DPad values from the ranges above.
         */
        if (!config->hw_dPad) {
            events_clr_bit(EV_KEY, KEY_DOWN);
            events_clr_bit(EV_KEY, KEY_UP);
            events_clr_bit(EV_KEY, KEY_LEFT);
            events_clr_bit(EV_KEY, KEY_RIGHT);
            events_clr_bit(EV_KEY, KEY_CENTER);
        }
    }

    /* configure EV_REL array
     *
     * EV_REL events are sent when the trackball is moved
     */
    if (config->hw_trackBall) {
        events_set_bit (EV_SYN, EV_REL );
        events_set_bits(EV_REL, REL_X, REL_Y);
    }

    /* configure EV_ABS array.
     *
     * EV_ABS events are sent when the touchscreen is pressed
     */
    if (config->hw_touchScreen) {
        int32_t*  values;

        events_set_bit (EV_SYN, EV_ABS );
        events_set_bits(EV_ABS, ABS_X, ABS_Z);
        /* Allocate the absinfo to report the min/max bounds for each
         * absolute dimension. The array must contain 3 tuples
         * of (min,max,fuzz,flat) 32-bit values.
         *
         * min and max are the bounds
         * fuzz corresponds to the device's fuziness, we set it to 0
         * flat corresponds to the flat position for JOEYDEV devices,
         * we also set it to 0.
         *
         * There is no need to save/restore this array in a snapshot
         * since the values only depend on the hardware configuration.
         */
        s->abs_info_count = 3*4;
        s->abs_info = values = malloc(sizeof(uint32_t)*s->abs_info_count);

        /* ABS_X min/max/fuzz/flat */
        values[0] = 0;
        values[1] = config->hw_lcd_width-1;
        values[2] = 0;
        values[3] = 0;
        values   += 4;

        /* ABS_Y */
        values[0] = 0;
        values[1] = config->hw_lcd_height-1;
        values[2] = 0;
        values[3] = 0;
        values   += 4;

        /* ABS_Z */
        values[0] = 0;
        values[1] = 1;
        values[2] = 0;
        values[3] = 0;
    }

    /* configure EV_SW array
     *
     * EW_SW events are sent to indicate that the keyboard lid
     * was closed or opened (done when we switch layouts through
     * KP-7 or KP-9).
     *
     * We only support this when hw.keyboard.lid is true.
     */
    if (config->hw_keyboard && config->hw_keyboard_lid) {
        events_set_bit(EV_SYN, EV_SW);
        events_set_bit(EV_SW, 0);
    }

    //cpu_register_physical_memory(base, 0xfff, iomemtype);

    android_if->qemu_add_kbd_event_handler(events_put_keycode, s);
    android_if->qemu_add_mouse_event_handler(events_put_mouse, s, 1, "goldfish-events");


    s->first = 0;
    s->last = 0;
    s->state = STATE_INIT;

}

static conf_object_t* new_goldfish_events_device(char* obj_name){
	goldfish_events_device* dev = skyeye_mm_zero(sizeof(goldfish_events_device));
	dev->obj = new_conf_object(obj_name, dev);
	events_state_t* events =  skyeye_mm_zero(sizeof(events_state_t));
	events->dev = dev;
	//events_state.events = qemu_new_events_ns(vm_clock, goldfish_events_tick, &events_state);

	dev->events = events;

	/* Register io function to the object */
	memory_space_intf* io_memory = skyeye_mm_zero(sizeof(memory_space_intf));
	io_memory->conf_obj = dev->obj;
	io_memory->read = events_read;
	io_memory->write = events_write;
	SKY_register_interface(io_memory, obj_name, MEMORY_SPACE_INTF_NAME);	

	general_signal_intf* events_signal = skyeye_mm_zero(sizeof(general_signal_intf));
	events_signal->conf_obj = NULL;
	events_signal->raise_signal = NULL;
	events_signal->lower_signal = NULL;
	dev->master = events_signal;
	dev->internal_irq = 1; /* Frame sync */
	dev->extern_irq = 11; /* Frame sync */
	SKY_register_interface(events_signal, obj_name, GENERAL_SIGNAL_INTF_NAME);


	events_dev_init();

	return dev->obj;
}
static void del_goldfish_events_device(conf_object_t* dev){
	
}

void goldfish_events_device_init()
{
}

void init_goldfish_events(){
	static skyeye_class_t class_data = {
		.class_name = "goldfish_events",
		.class_desc = "goldfish events",
		.new_instance = new_goldfish_events_device,
		.free_instance = del_goldfish_events_device,
		.get_attr = NULL,
		.set_attr = NULL
	};
	SKY_register_class(class_data.class_name,&class_data);
}

